#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include <string.h>
#include <strings.h>

#include "psd_route_settings.h"
#include "psd_radix.h"
#include "psd_logger.h"
#include "radix.h"

static radix_tree_t *incoming_route_settings = NULL;
static radix_tree_t *outgoing_route_settings = NULL;
static radix_tree_t *both_route_settings = NULL;
static pthread_mutex_t route_settings_lock;

int psd_route_settings_init() {
	if(pthread_mutex_init(&route_settings_lock, 0) != 0) { 
		psd_err(0, "pthread_mutex_init failed");
		goto error_out;
	}

	incoming_route_settings = New_Radix(32);
	if (!incoming_route_settings) {
		psd_err(0, "failed to create radix tree");
		goto error_out_mutex;
	}

	outgoing_route_settings = New_Radix(32);
	if (!outgoing_route_settings) {
		psd_err(0, "failed to create radix tree");
		goto error_out_incoming;
	}

	both_route_settings = New_Radix(32);
	if (!both_route_settings) {
		psd_err(0, "failed to create radix tree");
		goto error_out_outgoing;
	}


	return 0;

error_out_outgoing:
	Destroy_Radix(outgoing_route_settings, NULL);
error_out_incoming:
	Destroy_Radix(incoming_route_settings, NULL);
error_out_mutex:
	pthread_mutex_destroy(&route_settings_lock);
error_out:
	return -1;
}

int psd_route_settings_add(const char *addr_mask, enum psd_direction_t direction, const psdSettings *settings) {
	int retval;

	pthread_mutex_lock(&route_settings_lock);
	{
		switch (direction) {
			case PSD_INCOMING:
				retval = psd_radix_add(incoming_route_settings, addr_mask, settings);
				break;
			case PSD_OUTGOING:
				retval = psd_radix_add(outgoing_route_settings, addr_mask, settings);
				break;
			case PSD_BOTH:
				retval = psd_radix_add(both_route_settings, addr_mask, settings);
				break;
			default:
				retval = -1;
				break;
		}
	}
	pthread_mutex_unlock(&route_settings_lock);

	return retval;
}

psdSettings *psd_route_settings_remove(const char *addr_mask, enum psd_direction_t direction) {
	psdSettings *settings;

	pthread_mutex_lock(&route_settings_lock);
	{
		switch (direction) {
			case PSD_INCOMING:
				settings = psd_radix_remove(incoming_route_settings, addr_mask);
				break;
			case PSD_OUTGOING:
				settings = psd_radix_remove(outgoing_route_settings, addr_mask);
				break;
			case PSD_BOTH:
				settings = psd_radix_remove(both_route_settings, addr_mask);
				break;
			default:
				settings = NULL;
				break;
		}

	}
	pthread_mutex_unlock(&route_settings_lock);

	return settings;
}

psdSettings *psd_route_settings_lookup(const char *depot_id, enum psd_direction_t direction) {
	psdSettings *retval;

	pthread_mutex_lock(&route_settings_lock);
	{
		switch(direction) {
			case PSD_INCOMING:
				retval = psd_radix_lookup(incoming_route_settings, depot_id);
				break;
			case PSD_OUTGOING:
				retval = psd_radix_lookup(outgoing_route_settings, depot_id);
				break;
			case PSD_BOTH:
				retval = psd_radix_lookup(both_route_settings, depot_id);
				break;
			default:
				retval = NULL;
				break;
		}
	}
	pthread_mutex_unlock(&route_settings_lock);

	return retval;
}
